package com.huaqiang.tools.codegeneration;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import org.apache.commons.lang3.StringUtils;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author crelle
 * @auther zhangqiang
 * @date 2023/12/28
 */
public class CodeGeneration {
    //项目目录
    private final String basePath = System.getProperty("user.dir");

    //TODO 1、设置父包名
    private String parentPackageName = "";
    //模块名
    //TODO 2、设置模块名
    private String moduleName = "";

    //TODO 3、设置mapper xml的路径
    private String mapperXmlPath = "/src/main/resources/mapper";
    private String dbIp = "";
    private String dbPort = "";
    private String dbName = "";
    private String jdbcType = "mysql";
    //数据库用户名
    private String username = "";
    //数据库密码
    private String password = "";

    private String tablePrefix = "";
    private List<String> tableNames = null;
    //文件作者
    private final String author = "crelle";

    public String getParentPackageName() {
        return parentPackageName;
    }

    public void setParentPackageName(String parentPackageName) {
        this.parentPackageName = parentPackageName;
    }

    public String getModuleName() {
        return moduleName;
    }

    public void setModuleName(String moduleName) {
        this.moduleName = moduleName;
    }

    public String getMapperXmlPath() {
        return mapperXmlPath;
    }

    public void setMapperXmlPath(String mapperXmlPath) {
        this.mapperXmlPath = mapperXmlPath;
    }

    public String getDbIp() {
        return dbIp;
    }

    public void setDbIp(String dbIp) {
        this.dbIp = dbIp;
    }

    public String getDbPort() {
        return dbPort;
    }

    public void setDbPort(String dbPort) {
        this.dbPort = dbPort;
    }

    public String getDbName() {
        return dbName;
    }

    public void setDbName(String dbName) {
        this.dbName = dbName;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getTablePrefix() {
        return tablePrefix;
    }

    public void setTablePrefix(String tablePrefix) {
        this.tablePrefix = tablePrefix;
    }

    public List<String> getTableNames() {
        return tableNames;
    }

    public void setTableNames(List<String> tableNames) {
        this.tableNames = tableNames;
    }

    public String getJdbcType() {
        return jdbcType;
    }

    public void setJdbcType(String jdbcType) {
        this.jdbcType = jdbcType;
    }

    public void run() throws Exception {
        if (StringUtils.isBlank(parentPackageName)) {
            System.out.println("设置父包名不能为空!");
            throw new Exception("设置父包名不能为空!");
        }
        if (StringUtils.isBlank(moduleName)) {
            System.out.println("模块名不能为空!");
            throw new Exception("模块名不能为空!");
        }
        if (StringUtils.isBlank(mapperXmlPath)) {
            System.out.println("mapper xml的路径不能为空!");
            throw new Exception("mapper xml的路径不能为空!");
        }
        if (StringUtils.isBlank(dbIp)) {
            System.out.println("数据库ip或者域名不能为空!");
            throw new Exception("数据库ip或者域名不能为空!");
        }
        if (StringUtils.isBlank(dbPort)) {
            System.out.println("数据库端口不能为空!");
            throw new Exception("数据库端口不能为空!");
        }
        if (StringUtils.isBlank(dbName)) {
            System.out.println("数据库名不能为空!");
            throw new Exception("数据库名不能为空!");
        }
        if (StringUtils.isBlank(username)) {
            System.out.println("数据库用户名不能为空!");
            throw new Exception("数据库用户名不能为空!");
        }
        if (StringUtils.isBlank(password)) {
            System.out.println("数据库密码不能为空!");
            throw new Exception("数据库密码不能为空!");
        }
        if (StringUtils.isBlank(tablePrefix)) {
            System.out.println("表前缀不能为空!");
            throw new Exception("表前缀不能为空!");
        }
        if (tableNames.size() == 0) {
            System.out.println("表名不能为空!");
            throw new Exception("表名不能为空!");
        }
        System.out.println("开始生成代码");
        String url = "jdbc:" + jdbcType + "://" + dbIp + ":" + dbPort + "/" + dbName + "?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai";

        //1、配置数据源
        FastAutoGenerator.create(url, username, password)
                //2、全局配置
                .globalConfig(builder -> {
                    builder.author(author) // 设置作者名
                            .outputDir(basePath + "/src/main/java")   //设置输出路径
                            .commentDate("yyyy-MM-dd hh:mm:ss")   //注释日期
                            .dateType(DateType.ONLY_DATE)   //定义生成的实体类中日期的类型 TIME_PACK=LocalDateTime;ONLY_DATE=Date;
//                            .enableSwagger()   //开启 swagger 模式
                            .disableOpenDir();   //禁止打开输出目录，默认打开
                })
                //3、包配置
                .packageConfig(builder -> {
                    builder.parent(parentPackageName) // 设置父包名
                            .moduleName(moduleName)   //设置模块包名
                            .entity("pojo.entity")   //pojo 实体类包名
                            .service("service") //Service 包名
                            .serviceImpl("service.impl") // ***ServiceImpl 包名
                            .mapper("mapper")   //Mapper 包名
                            .xml("mapper")  //Mapper XML 包名
                            .controller("controller") //Controller 包名
                            .pathInfo(Collections.singletonMap(OutputFile.xml, basePath + mapperXmlPath));    //配置 **Mapper.xml 路径信息：项目的 resources 目录的 Mapper 目录下
                })
                // 使用自定义模板
                .templateConfig(builder -> {
                    builder.entity("templates/entity.java")   // 自定义 Entity 模板
                            .mapper("templates/mapper.java") // 自定义 mapper 模板
                            .xml("templates/mapper.xml")
                            .service("templates/service.java")   // 自定义 Service 模板
                            .serviceImpl("templates/serviceImpl.java") // 自定义 ServiceImpl 模板
                            .controller("templates/controller.java"); // 自定义 Controller 模板
                })
                //自定义配置
                .injectionConfig(injectConfig -> {
                    Map<String, Object> customMap = new HashMap<>();
                    customMap.put("customDtoPackage", parentPackageName + "." + moduleName + "." + "pojo.dto");
                    customMap.put("customQueryPackage", parentPackageName + "." + moduleName + "." + "pojo.query");
                    customMap.put("customVoPackage", parentPackageName + "." + moduleName + "." + "pojo.vo");
                    customMap.put("customBasePackage", parentPackageName + "." + moduleName);
                    injectConfig.customMap(customMap); //注入自定义属性
                    // 添加 Query.java 生成
                    injectConfig.customFile(new CustomFile.Builder()
                            .fileName("Query.java") //文件名称
                            .templatePath("templates/entityQuery.java.ftl") //指定生成模板路径
                            .packageName("pojo.query") //包名,自3.5.10开始,可通过在package里面获取自定义包全路径,低版本下无法获取,示例:package.entityDTO
                            .build());
                    // 添加 Vo.java 生成
                    injectConfig.customFile(new CustomFile.Builder()
                            .fileName("Vo.java") // VO 文件
                            .templatePath("templates/entityVo.java.ftl") // VO 对应的模板路径
                            .packageName("pojo.vo") // VO 的包路径
                            .build());
                    // 添加 Dto.java 生成
                    injectConfig.customFile(new CustomFile.Builder()
                            .fileName("Dto.java") // VO 文件
                            .templatePath("templates/entityDto.java.ftl") // VO 对应的模板路径
                            .packageName("pojo.dto") // VO 的包路径
                            .build());
                })
                //4、策略配置
                .strategyConfig(builder -> {
                    //TODO 5、设置表名
                    builder.addInclude(String.join(",", tableNames)) // 设置需要生成的数据表名
                            .addTablePrefix(tablePrefix) // 设置过滤表前缀
                            //4.1、Mapper策略配置
                            .mapperBuilder().superClass(BaseMapper.class)   //设置父类
                            .enableFileOverride()
                            .formatMapperFileName("%sMapper")   //格式化 mapper 文件名称
                            .enableMapperAnnotation()       //开启 @Mapper 注解
                            .formatXmlFileName("%sMapper") //格式化 Xml 文件名称
                            .enableBaseColumnList()     //生成baseColumn
                            .enableBaseResultMap()      //生成baseResultMap
                            .enableMapperAnnotation()

                            //4.2、service 策略配置
                            .serviceBuilder().formatServiceFileName("%sService") //格式化 service 接口文件名称，%s进行匹配表名，如 UserService
                            .enableFileOverride()
                            .formatServiceImplFileName("%sServiceImpl") //格式化 service 实现类文件名称，%s进行匹配表名，如 UserServiceImpl

                            //4.4、Controller策略配置
                            .controllerBuilder().formatFileName("%sController") //格式化 Controller 类文件名称，%s进行匹配表名，如 UserController
                            .enableFileOverride()
                            .enableRestStyle() //开启生成 @RestController 控制器

                            //4.3、实体类策略配置
                            .entityBuilder().enableLombok() //开启 Lombok
                            .disableSerialVersionUID()  //不实现 Serializable 接口，不生产 SerialVersionUID
                            .enableFileOverride()
                            .idType(IdType.ASSIGN_UUID).logicDeleteColumnName("deleted")   //逻辑删除字段名
                            .naming(NamingStrategy.underline_to_camel)  //数据库表映射到实体的命名策略：下划线转驼峰命
                            .columnNaming(NamingStrategy.underline_to_camel)    //数据库表字段映射到实体的命名策略：下划线转驼峰命
                            .addTableFills(new Column("create_time", FieldFill.INSERT), new Column("modify_time", FieldFill.INSERT_UPDATE))   //添加表字段填充，"create_time"字段自动填充为插入时间，"modify_time"字段自动填充为插入修改时间
                            .enableTableFieldAnnotation();      // 开启生成实体时生成字段注解

                })
                // 使用 FreeMarker 模板引擎（默认是 Velocity）
                .templateEngine(new FreemarkerTemplateEngine())
                .execute();
    }

}
